import React, { FC, useEffect, useState, createContext } from 'react'
interface ISetXState<T> {
  // 这里定义的是普通函数，但是这个函数在声明之前必须传入定义
  (state: T, callback: (state: T) => void): void
}

// 当泛型写在函数的参数的前面，表示这个泛型需要调用函数时定义
interface IUseXState {
  // 这里定义的是泛型函数
  <T>(initState: T): [T, ISetXState<T>]
}

// 自定义hook
// 官方没有要求，但是业界约定俗成的规则，所有的hook都必须以use开头
// 为了改变useState，我们需要自定义一个叫useXState的方法
// 定义一个方法叫useXState,它要实现这样的一个功能：接收一个状态作为参数，返回状态本身和修改状态的方法
const useXState: IUseXState = initState => {
  // 为了状态变化能被react感知，我们需要在这里使用useState来管理状态
  const [state, setState] = useState(initState)
  // 这个修改状态的方法要能像setState一样，接收两个参数，第一个参数是状态，第二个参数是一个回调函数，我们可以回调函数中获取最新的状态
  // 这里的setAa就是类组件中的setState，只不过它是针对aa这个状态的，所以我们需要对它进行改造
  // useState是react内部定义的，我们无法改变，于是。为了改造setAa,我们需要先改造useState
  return [
    state,
    // 返回的第二个函数必须要能够像this.setState一样，能够接两个参数
    // 第一个参数是我们需要改变的值
    // 第二个参数是我们的状态发生变化时能够通知组件相应的变化 的一个回调函数
    (newState, callback) => {
      // 如果传入的状态是一个方法，执行方法后再赋值
      // 注意：ts不支持使用Object.toString方法来缩小类型范围
      // if (Object.prototype.toString.call(newState) === '[object Function]') {
      if (typeof newState === 'function') {
        newState = newState()
      }
      // 我们要能够真正地去改变state的内容
      setState(newState)
      // 然后，调用callback去返回当前的最新状态
      callback(newState)
    },
  ]
}

// FC是函数组件的类型声明，从react中直接导入即可
const Index: FC = () => {
  const [aa, setAa] = useXState(() => 'v' + Date.now().toString(36).slice(2))
  // 然后，这个修改状态的方法要能像setState一样，接收两个参数，第一个参数是状态，第二个参数是一个回调函数，我们可以回调函数中获取最新的状态
  useEffect(() => {
    // 更新aa里的值
    setAa(
      // 新的aa的值
      () => 'v' + Date.now().toString(36).slice(2),
      // 想要实时获取aa的值的方法
      aa => {
        // 在改变当前状态之前会执行这个函数
        setTimeout(() => {
          console.log(aa, '回调函数里获取到的aa的值')
        }, 1000)
      }
    )
  }, [])

  useEffect(() => {
    console.log(aa, '正常获取到的aa的值')
  }, [aa])
  return <div>fsdadfasdfasdfasdfasdf</div>
}
export default Index
